package com.touchealth.test.touchealth;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 光大签名工具
 * @author zhangyanqiang
 * @date 2020/9/21
 **/
public class SignatureUtil {

    private static final String KEY_ALGORITHM = "RSA";
    private static final String SIGN_ALGORITHM = "MD5withRSA";
    private static final int RSA_KEY_SIZE = 1024;
    private static final int MAX_ENCRYPT_BLOCK = 117;
    private static final int MAX_DECRYPT_BLOCK = 128;

    public static String formatString(String source) {
        if (source == null) { return null; }
        return source.replaceAll("\\r", "").replaceAll("\\n", "");
    }

    public static KeyPair getKeyPair() throws Exception {

        KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        generator.initialize(RSA_KEY_SIZE);
        KeyPair keyPair = generator.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 得到公钥
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
        return keyPair;
    }

    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

    public static boolean verify(PublicKey publicKey, String srcData, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initVerify(key); signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    public static String encrypt(String str, String publicKey) throws Exception {
        publicKey = formatString(publicKey);
        //base64编码的公钥
         byte[] decoded = Base64.decodeBase64(publicKey);
         RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
         //RSA加密
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] bytes = str.getBytes();
        int inputLen = bytes.length;
        //偏移量
         int offLen = 0;
         int i = 0;
         ByteArrayOutputStream bops = new ByteArrayOutputStream();
         while(inputLen - offLen > 0){
             byte [] cache;
             if(inputLen - offLen > 117){
                 cache = cipher.doFinal(bytes, offLen,117);
             }else{
                 cache = cipher.doFinal(bytes, offLen,inputLen - offLen);
             }
             bops.write(cache);
             i++; offLen = 117 * i;
         }
         bops.close();
         byte[] encryptedData = bops.toByteArray();
         return Base64.encodeBase64String(encryptedData);
    }

    public static String decrypt(String str, String privateKey) throws Exception {
        privateKey = formatString(privateKey);
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        // base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
        // RSA解密
         Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
         cipher.init(Cipher.DECRYPT_MODE, priKey);
         int inputLen = inputByte.length;
         int offLen = 0;
         int i = 0;
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         while(inputLen - offLen > 0){
             byte[] cache;
             if(inputLen - offLen > 128){
                 cache = cipher.doFinal(inputByte, offLen,128);
             }else{
                 cache = cipher.doFinal(inputByte, offLen,inputLen - offLen);
             }
             byteArrayOutputStream.write(cache);
             i++; offLen = 128 * i;
         }
         byteArrayOutputStream.close();
         byte[] byteArray = byteArrayOutputStream.toByteArray();
         return new String(byteArray);
    }

    public static String encryptByPrivateKey(String data, String privateKey) {
        try {
            privateKey = formatString(privateKey);
            byte[] kb = Base64.decodeBase64(privateKey.getBytes(StandardCharsets.UTF_8));
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(kb);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey key = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
            int inputLen = bytes.length;
            //偏移量
            int offLen = 0;
            int i = 0;
            ByteArrayOutputStream bops = new ByteArrayOutputStream();
            while (inputLen - offLen > 0) {
                byte[] cache;
                if (inputLen - offLen > 117) {
                    cache = cipher.doFinal(bytes, offLen, 117);
                } else {
                    cache = cipher.doFinal(bytes, offLen, inputLen - offLen);
                }
                bops.write(cache);
                i++;
                offLen = 117 * i;
            }
            bops.close();
            byte[] encryptedData = bops.toByteArray();
            return Base64.encodeBase64String(encryptedData);
        } catch (Exception e) {
        }
        return null;
    }

    public static String decryptByPublicKey(String data, String publicKey) {
        try {
            publicKey = formatString(publicKey);
            byte[] kb = Base64.decodeBase64(publicKey.getBytes(StandardCharsets.UTF_8));
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(kb);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey key = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] bytes = Base64.decodeBase64(data);
            int inputLen = bytes.length;
            int offLen = 0;
            int i = 0;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while(inputLen - offLen > 0){
                byte[] cache;
                if(inputLen - offLen > 128){
                    cache = cipher.doFinal(bytes,offLen,128);
                }else{
                    cache = cipher.doFinal(bytes,offLen,inputLen - offLen);
                }
                byteArrayOutputStream.write(cache);
                i++;
                offLen = 128 * i;
            }
            byteArrayOutputStream.close();
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            return new String(byteArray);
        } catch (Exception e) {
            throw new RuntimeException("Failed to decryptByPublicKey data.");
        }
    }

    public static byte[] decryptByPublicKey(byte[] bytes, String publicKey) {
        try {
            publicKey = formatString(publicKey);
            byte[] kb = Base64.decodeBase64(publicKey.getBytes(StandardCharsets.UTF_8));
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(kb);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey key = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, key);
            int inputLen = bytes.length;
            int offLen = 0;
            int i = 0;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while(inputLen - offLen > 0){
                byte[] cache;
                if(inputLen - offLen > 128){
                    cache = cipher.doFinal(bytes,offLen,128);
                }else{
                    cache = cipher.doFinal(bytes,offLen,inputLen - offLen);
                }
                byteArrayOutputStream.write(cache);
                i++;
                offLen = 128 * i;
            }
            byteArrayOutputStream.close();
            return byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("Failed to decryptByPublicKey data.");
        }
    }

}
